#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#

import os, socket, time, traceback


servers_list = []

enable_debug = False
enable_trace = False


class   Logger( object ) :

    def __init__(self) :
        self.pid = str(os.getpid())
        self.thread = '-'
        self.hostname = os.uname()[1]

        self._udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._tcp_sockets = {}
        #### __init__()

    def log(self, category, level, msg) :
        now = int(time.time())
        filename,lineno,module,funcname = self.__tracestack()
        fields = [ str(now), self.hostname, self.pid, self.thread, category, level, filename, str(lineno), msg, '\r\n' ]
        #print('{0} {1} {2} {3} {4} {5} {6} {7} {8}'.format( *fields ))

        record = '\t'.join( fields )
        record_bytes = bytes( record, 'utf8' )

        global  servers_list

        # in TCP mode
        for addr in servers_list :
            #print(' - send to {0} ...'.format(addr))
            self.__tcp_sendto( record_bytes, addr )

        ''' in UDP mode
        for addr in servers_list :
            #print(' - send to {0} ...'.format(addr))
            try :
                self._udp_socket.sendto( bytes(record, 'utf8'), addr )
            except :
                pass
        '''
        #### log()

    def error(self, category, msg) :
        return  self.log( category, 'ERROR', msg )
        #### error()

    def warn(self, category, msg) :
        return  self.log( category, 'WARN', msg )
        #### warn()

    def info(self, category, msg) :
        return  self.log( category, 'INFO', msg )
        #### info()

    def debug(self, category, msg) :
        global  enable_debug

        if enable_debug :
            return  self.log( category, 'DEBUG', msg )

        return  None
        #### debug()

    def trace(self, category, msg) :
        global  enable_trace

        if enable_trace :
            return  self.log( category, 'TRACE', msg )

        return  None
        #### error()

    def __tracestack(self) :
        stack = traceback.extract_stack()
        LAYER = -4
        filename = os.path.basename(stack[LAYER][0])
        lineno   = stack[LAYER][1]
        module   = stack[LAYER][2]
        function = stack[LAYER][3]
        return  (filename, lineno, module, function)
        ## __tracestack()


    ###################################################################

    def __getTCPSocket(self, addr)  :
        if addr in self._tcp_sockets :
            sock = self._tcp_sockets[ addr ]
            if sock :
                return  sock

        try :
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout( 0.5 )
            sock.connect( addr )
        except :
            sock = None

        self._tcp_sockets[ addr ] = sock
        return  sock
        #### __getTCPSocket()

    def __tcp_sendto(self, data_bytes, addr) :
        tcp_socket = self.__getTCPSocket( addr )
        if tcp_socket is None :
            return  False

        caught_except = False
        MAX_RETRIES = 5
        retry = 0
        while retry < MAX_RETRIES :
            try :
                n = tcp_socket.send( data_bytes )
                #print( 'send {0} {1} bytes'.format(data_bytes, n) )
                data_bytes = data_bytes[ n: ]
                if not data_bytes : break
            except :
                caught_except = True
                break
            retry += 1
        if (retry == MAX_RETRIES) or caught_except :
            tcp_socket.close()
            tcp_socket = None
            self._tcp_sockets[ addr ] = None
            return  False

        return  True
        #### __tcp_sendto

    ####    class Logger


_logger = Logger()


def getLogger() :
    return  _logger
    #### getLogger()

def setServerList( s_list ) :
    global  servers_list

    servers_list = s_list
    #### setServerList()


if  __name__ == '__main__' :
    setServerList( [ ('127.0.0.1', 8844), ('127.0.0.1', 4344) ] )

    logger = getLogger()

    logger.error( 'catx', 'CATX error message' )
    logger.info( 'catx', 'CATX info message' )
    logger.debug( 'catx', 'CATX debug message' )


